home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 97 / CD-ROM 97 / CD-ROM 97.iso / internet / ghostzilla / ghsetup.exe / chrome / chatzilla.jar / content / chatzilla / lib / js / irc.js < prev    next >
Encoding:
Text File  |  2002-05-01  |  51.2 KB  |  2,249 lines

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *   
  3.  * The contents of this file are subject to the Mozilla Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/MPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is JSIRC Library
  14.  *
  15.  * The Initial Developer of the Original Code is New Dimensions Consulting,
  16.  * Inc. Portions created by New Dimensions Consulting, Inc. are
  17.  * Copyright (C) 1999 New Dimenstions Consulting, Inc. All
  18.  * Rights Reserved.
  19.  *
  20.  * Contributor(s): 
  21.  *
  22.  *
  23.  * Contributor(s):
  24.  *  Robert Ginda, rginda@ndcico.com, original author
  25.  *
  26.  ****
  27.  *
  28.  * depends on utils.js, events.js, and connection.js
  29.  *
  30.  * IRC(RFC 1459) library.
  31.  * Contains the following classes:
  32.  *
  33.  * CIRCNetwork
  34.  * Networtk object.  Takes care of logging into a "primary" server given a
  35.  * list of potential hostnames for an IRC network.  (among other things.)
  36.  *
  37.  * CIRCServer
  38.  * Server object.  Requires an initialized bsIConnection object for
  39.  * communicating with the irc server.
  40.  * Server.sayTo queues outgoing PRIVMSGs for sending to the server.  Using
  41.  * sayTo takes care not to send lines faster than one every 1.5 seconds.
  42.  * Server.connection.sendData sends raw lines over the connection, avoiding the
  43.  * queue.
  44.  *
  45.  * CIRCUser
  46.  * User objects.  Children of server objects.
  47.  *
  48.  * CIRCChannel
  49.  * Channel object.  Children of server objects.
  50.  *
  51.  * CIRCChanMode
  52.  * Channel mode object.  Children of channel objects
  53.  *
  54.  * CIRCChanUser
  55.  * Channel User objects.  Children of channel objects, with __proto__ set
  56.  * to a CIRCUser object (automatically.)
  57.  *
  58.  * 1999-09-15 rginda@ndcico.com           v1.0
  59.  *
  60.  */
  61.  
  62. const JSIRC_ERR_NO_SOCKET = "JSIRCE:NS";
  63. const JSIRC_ERR_EXHAUSTED = "JSIRCE:E";
  64.  
  65. function userIsMe (user)
  66. {
  67.     
  68.     switch (user.TYPE)
  69.     {
  70.         case "IRCUser":
  71.             return (user == user.parent.me);
  72.             break;
  73.             
  74.         case "IRCChanUser":
  75.             return (user.__proto__ == user.parent.parent.me);
  76.             break;
  77.             
  78.         default:
  79.             return false;
  80.             
  81.     }
  82.  
  83.     return false;
  84. }
  85.  
  86. /*
  87.  * irc network
  88.  */
  89. function CIRCNetwork (name, serverList, eventPump)
  90. {
  91.  
  92.     this.name = name;
  93.     this.serverList = serverList;
  94.     this.eventPump = eventPump;
  95.     this.servers = new Object();
  96.  
  97. }
  98.  
  99. /** Clients should override this stuff themselves **/
  100. CIRCNetwork.prototype.INITIAL_NICK = "js-irc";
  101. CIRCNetwork.prototype.INITIAL_NAME = "INITIAL_NAME";
  102. CIRCNetwork.prototype.INITIAL_DESC = "INITIAL_DESC";
  103. CIRCNetwork.prototype.INITIAL_CHANNEL = "#jsbot"; 
  104. /* set INITIAL_CHANNEL to "" if you don't want a primary channel */
  105.  
  106. CIRCNetwork.prototype.MAX_CONNECT_ATTEMPTS = 5;
  107. CIRCNetwork.prototype.stayingPower = false; 
  108.  
  109. CIRCNetwork.prototype.TYPE = "IRCNetwork";
  110.  
  111. CIRCNetwork.prototype.getURL =
  112. function net_geturl ()
  113. {
  114.     if (this.serverList.length == 1 &&
  115.         this.serverList[0].name == this.name &&
  116.         this.serverList[0].port != 6667)
  117.     {
  118.         return "irc://" + this.serverList[0].name + ":" +
  119.             this.serverList[0].port + "/";
  120.     }
  121.     
  122.     return "irc://" + escape(this.name) + "/";
  123. }
  124.  
  125. CIRCNetwork.prototype.connect =
  126. function net_conenct()
  127. {
  128.  
  129.     if ("primServ" in this && this.primServ.connection.isConnected)
  130.         return;
  131.  
  132.     this.connecting = true; /* connection is considered "made" when serve
  133.                              * sends a 001 message (see server.on001) */
  134.     this.connectAttempt = 0;
  135.     this.nextHost = 0;
  136.     var ev = new CEvent ("network", "do-connect", this, "onDoConnect");
  137.     this.eventPump.addEvent (ev);
  138.  
  139. }
  140.  
  141. CIRCNetwork.prototype.quit =
  142. function net_quit (reason)
  143. {
  144.  
  145.     this.stayingPower = false;
  146.     if (this.isConnected())
  147.         this.primServ.logout (reason);
  148.  
  149. }
  150.  
  151. /*
  152.  * Handles a request to connect to a primary server.
  153.  */
  154. CIRCNetwork.prototype.onDoConnect =
  155. function net_doconnect(e)
  156. {
  157.     var c;
  158.     
  159.     if ("primServ" in this && this.primServ.connection.isConnected)
  160.         return true;
  161.  
  162.     var ev;
  163.  
  164.     if (this.connectAttempt++ >= this.MAX_CONNECT_ATTEMPTS)
  165.     {
  166.         ev = new CEvent ("network", "error", this, "onError");
  167.         ev.server = this;
  168.         ev.debug = "Connection attempts exhausted, giving up.";
  169.         ev.errorCode = JSIRC_ERR_EXHAUSTED;
  170.         this.eventPump.addEvent (ev);        
  171.         return false;
  172.     }
  173.  
  174.     try
  175.     {
  176.         c = new CBSConnection();
  177.     }
  178.     catch (ex)
  179.     {
  180.         ev = new CEvent ("network", "error", this, "onError");
  181.         ev.server = this;
  182.         ev.debug = "Couldn't create socket :" + ex;
  183.         ev.errorCode = JSIRC_ERR_NO_SOCKET;
  184.         ev.exception = ex;
  185.         this.eventPump.addEvent (ev);
  186.         return false;
  187.     }
  188.  
  189.     var host = this.nextHost++;
  190.     if (host >= this.serverList.length)
  191.     {
  192.         this.nextHost = 1;
  193.         host = 0;
  194.     }
  195.  
  196.     ev = new CEvent ("network", "startconnect", this, "onStartConnect");
  197.     ev.debug = "Connecting to " + this.serverList[host].name + ":" +    
  198.                this.serverList[host].port + ", attempt " + this.connectAttempt +
  199.                " of " + this.MAX_CONNECT_ATTEMPTS + "...";
  200.     ev.host = this.serverList[host].name;
  201.     ev.port = this.serverList[host].port;
  202.     ev.connectAttempt = this.connectAttempt;
  203.     this.eventPump.addEvent (ev);
  204.  
  205.     var connected = false;
  206.     
  207.     if (c.connect (this.serverList[host].name, this.serverList[host].port,
  208.                    (void 0), true, null))
  209.     {
  210.         var ex;
  211.         ev = new CEvent ("network", "connect", this, "onConnect");
  212.         try
  213.         {
  214.             var password = ("password" in this.serverList[host]) ?
  215.                 this.serverList[host].password : "";
  216.                 
  217.             ev.server = this.primServ = new CIRCServer (this, c, password);
  218.             this.eventPump.addEvent (ev);
  219.             connected = true;
  220.         }
  221.         catch (ex)
  222.         {
  223.             dd ("Caught following exception creating new CIRCServer in " +
  224.                 "CIRCNetwork::onDoConnect().\n" + dumpObjectTree(ex));
  225.         }
  226.     }
  227.     
  228.  
  229.     if (!connected)
  230.     { /* connect failed, try again  */
  231.         ev = new CEvent ("network", "do-connect", this, "onDoConnect");
  232.         this.eventPump.addEvent (ev);
  233.     }
  234.  
  235.     return true;
  236.  
  237. }
  238.  
  239.  
  240. /*
  241.  * What to do when the client connects to it's primary server
  242.  */
  243. CIRCNetwork.prototype.onConnect = 
  244. function net_connect (e)
  245. {
  246.     this.primServ = e.server;
  247.     this.primServ.login (this.INITIAL_NICK, this.INITIAL_NAME,
  248.                          this.INITIAL_DESC);
  249.     return true;
  250.  
  251. }
  252.  
  253. CIRCNetwork.prototype.isConnected = 
  254. function net_connected (e)
  255. {
  256.     return ("primServ" in this && this.primServ.connection.isConnected);   
  257. }
  258.  
  259. /*
  260.  * irc server
  261.  */ 
  262. function CIRCServer (parent, connection, password)
  263. {
  264.     var serverName = connection.host + ":" + connection.port;
  265.     var s;
  266.     if (serverName in parent.servers)
  267.     {
  268.         s = parent.servers[serverName];
  269.     }
  270.     else
  271.     {
  272.         s = this;
  273.         s.channels = new Object();
  274.         s.users = new Object();
  275.     }
  276.     
  277.     s.name = serverName;
  278.     s.parent = parent;
  279.     s.password = password;
  280.     s.connection = connection;
  281.     s.sendQueue = new Array();
  282.     s.lastSend = new Date("1/1/1980");
  283.     s.sendsThisRound = 0;
  284.     s.savedLine = "";
  285.     s.lag = -1;    
  286.     s.usersStable = true;
  287.  
  288.     if (jsenv.HAS_NSPR_EVENTQ)
  289.         connection.startAsyncRead(s);
  290.     else
  291.         s.parent.eventPump.addEvent(new CEvent ("server", "poll", s,
  292.                                                 "onPoll"));
  293.  
  294.     parent.servers[serverName] = s;
  295.     return s;
  296.     
  297. }
  298.  
  299. CIRCServer.prototype.MAX_LINES_PER_SEND = 0; /* unlimited */
  300. CIRCServer.prototype.MS_BETWEEN_SENDS = 1500;
  301. CIRCServer.prototype.READ_TIMEOUT = 100;
  302. CIRCServer.prototype.TOO_MANY_LINES_MSG = "\01ACTION has said too much\01";
  303. CIRCServer.prototype.VERSION_RPLY = "JS-IRC Library v0.01, " +
  304.     "Copyright (C) 1999 Robert Ginda; rginda@ndcico.com";
  305. CIRCServer.prototype.DEFAULT_REASON = "no reason";
  306.  
  307. CIRCServer.prototype.TYPE = "IRCServer";
  308.  
  309. CIRCServer.prototype.getURL =
  310. function serv_geturl ()
  311. {
  312.     return this.parent.getURL();
  313. }
  314.  
  315. CIRCServer.prototype.onStreamDataAvailable = 
  316. function serv_sda (request, inStream, sourceOffset, count)
  317. {
  318.     var ev = new CEvent ("server", "data-available", this,
  319.                          "onDataAvailable");
  320.  
  321.     ev.line = this.connection.readData(0, count);
  322.     /* route data-available as we get it.  the data-available handler does
  323.      * not do much, so we can probably get away with this without starving
  324.      * the UI even under heavy input traffic.
  325.      */
  326.     this.parent.eventPump.routeEvent (ev);
  327. }
  328.  
  329. CIRCServer.prototype.onStreamClose = 
  330. function serv_sockdiscon(status)
  331. {
  332.     
  333.     this.connection.isConnected = false;
  334.  
  335.     var ev = new CEvent ("server", "disconnect", this, "onDisconnect");
  336.     ev.server = this;
  337.     ev.disconnectStatus = status;
  338.     this.parent.eventPump.addEvent (ev);
  339.     
  340. }
  341.  
  342.     
  343. CIRCServer.prototype.flushSendQueue =
  344. function serv_flush()
  345. {
  346.  
  347.     this.sendQueue.length = 0;
  348.     dd("sendQueue flushed.");
  349.  
  350.     return true;
  351.  
  352. }
  353.  
  354. CIRCServer.prototype.login =
  355. function serv_login(nick, name, desc)
  356. {
  357.  
  358.     this.me = new CIRCUser (this, nick, name);
  359.     if (this.password)
  360.        this.sendData ("PASS " + this.password + "\n");
  361.     this.sendData ("NICK " + nick + "\n");
  362.     this.sendData ("USER " + name + " foo bar :" + desc + "\n");
  363.     
  364. }
  365.  
  366. CIRCServer.prototype.logout =
  367. function serv_logout(reason)
  368. {
  369.     
  370.     if (typeof reason == "undefined") reason = this.DEFAULT_REASON;
  371.  
  372.     this.connection.sendData ("QUIT :" + reason + "\n");
  373.     this.connection.disconnect();
  374.  
  375. }
  376.  
  377. CIRCServer.prototype.addChannel =
  378. function serv_addchan (name)
  379. {
  380.  
  381.     return new CIRCChannel (this, name);
  382.     
  383. }
  384.     
  385. CIRCServer.prototype.addUser =
  386. function serv_addusr (nick, name, host)
  387. {
  388.  
  389.     return new CIRCUser (this, nick, name, host);
  390.     
  391. }
  392.     
  393. CIRCServer.prototype.getChannelsLength =
  394. function serv_chanlen()
  395. {
  396.     var i = 0;
  397.  
  398.     for (var p in this.channels)
  399.         i++;
  400.  
  401.     return i;
  402.     
  403. }
  404.  
  405. CIRCServer.prototype.getUsersLength =
  406. function serv_chanlen()
  407. {
  408.     var i = 0;
  409.     
  410.     for (var p in this.users)
  411.         i++;
  412.  
  413.     return i;
  414.     
  415. }
  416.  
  417. CIRCServer.prototype.sendData =
  418. function serv_senddata (msg)
  419. {
  420.     
  421.     this.queuedSendData (msg);
  422.         
  423. }
  424.  
  425. CIRCServer.prototype.queuedSendData =
  426. function serv_senddata (msg)
  427. {
  428.     
  429.     if (this.sendQueue.length == 0)
  430.         this.parent.eventPump.addEvent (new CEvent ("server", "senddata",
  431.                                                     this, "onSendData"));
  432.     arrayInsertAt (this.sendQueue, 0, new String(msg));
  433.         
  434. }
  435.  
  436. /*
  437.  * Takes care not to let more than MAX_LINES_PER_SEND lines out per
  438.  * cycle.  Cycle's are defined as the time between onPoll calls.
  439.  */
  440. CIRCServer.prototype.messageTo =
  441. function serv_messto (code, target, msg, ctcpCode)
  442. {
  443.     var lines = String(msg).split ("\n");
  444.     var sendable = 0, i;
  445.     var pfx = "", sfx = "";
  446.  
  447.     if (this.MAX_LINES_PER_SEND &&
  448.         this.sendsThisRound > this.MAX_LINES_PER_SEND)
  449.         return false;
  450.  
  451.     if (ctcpCode)
  452.     {
  453.         pfx = "\01" + ctcpCode;
  454.         sfx = "\01";
  455.     }
  456.  
  457.     for (i in lines)
  458.         if ((lines[i] != "") || ctcpCode) sendable++;
  459.  
  460.     for (i in lines)
  461.     {
  462.         if (this.MAX_LINES_PER_SEND && (
  463.             ((this.sendsThisRound == this.MAX_LINES_PER_SEND - 1) &&
  464.              (sendable > this.MAX_LINES_PER_SEND)) ||
  465.             this.sendsThisRound == this.MAX_LINES_PER_SEND))
  466.         {
  467.             this.sendData ("PRIVMSG " + target + " :" +
  468.                            this.TOO_MANY_LINES_MSG + "\n");
  469.             this.sendsThisRound++;
  470.             return true;
  471.         }
  472.             
  473.         if ((lines[i] != "") || ctcpCode)
  474.         {
  475.             var line = code + " " + target + " :" + pfx;
  476.             this.sendsThisRound++;
  477.             if (lines[i] != "")
  478.             {
  479.                 if (ctcpCode)
  480.                     line += " ";
  481.                 line += lines[i] + sfx;
  482.             }
  483.             else
  484.                 line += sfx;
  485.             //dd ("-*- irc sending '" +  line + "'");
  486.             this.sendData (line + "\n");
  487.         }
  488.         
  489.     }
  490.  
  491.     return true;        
  492. }
  493.  
  494. CIRCServer.prototype.sayTo = 
  495. function serv_sayto (target, msg)
  496. {
  497.  
  498.     this.messageTo ("PRIVMSG", target, msg);
  499.  
  500. }
  501.  
  502. CIRCServer.prototype.noticeTo = 
  503. function serv_noticeto (target, msg)
  504. {
  505.  
  506.     this.messageTo ("NOTICE", target, msg);
  507.  
  508. }
  509.  
  510. CIRCServer.prototype.actTo = 
  511. function serv_actto (target, msg)
  512. {
  513.  
  514.     this.messageTo ("PRIVMSG", target, msg, "ACTION");
  515.  
  516. }
  517.  
  518. CIRCServer.prototype.ctcpTo = 
  519. function serv_ctcpto (target, code, msg, method)
  520. {
  521.     if (typeof msg == "undefined")
  522.         msg = "";
  523.  
  524.     if (typeof method == "undefined")
  525.         method = "PRIVMSG";
  526.     
  527.     code = code.toUpperCase();
  528.     if (code == "PING" && !msg)
  529.         msg = Number(new Date());
  530.     this.messageTo (method, target, msg, code);
  531.  
  532. }
  533.  
  534. /**
  535.  * Abstracts the whois command.
  536.  *
  537.  * @param target        intended user(s).
  538.  */
  539. CIRCServer.prototype.whois = 
  540. function serv_whois (target) 
  541. {
  542.  
  543.     this.sendData ("WHOIS " + target + "\n");
  544.  
  545. }
  546.  
  547. CIRCServer.prototype.onDisconnect = 
  548. function serv_disconnect(e)
  549. {
  550.  
  551.     if (("connecting" in this.parent) ||
  552.         /* fell off while connecting, try again */
  553.         (this.parent.primServ == this) && (this.parent.stayingPower))
  554.     { /* fell off primary server, reconnect to any host in the serverList */
  555.     var ev = new CEvent ("network", "do-connect", this.parent,
  556.                              "onDoConnect");
  557.     this.parent.eventPump.addEvent (ev);
  558.     }
  559.  
  560.     e.server = this;
  561.     e.set = "network";
  562.     e.destObject = this.parent;
  563.  
  564.     for (var c in this.channels)
  565.         this.channels[c].users = new Object();
  566.  
  567.     return true;
  568.  
  569. }
  570.  
  571. CIRCServer.prototype.onSendData =
  572. function serv_onsenddata (e)
  573. {
  574.     if (!this.connection.isConnected)
  575.     {
  576.         dd ("Can't send to disconnected socket");
  577.         this.flushSendQueue();
  578.         return false;
  579.     }
  580.         
  581.     var d = new Date();
  582.  
  583.     this.sendsThisRound = 0;
  584.  
  585.     if (((d - this.lastSend) >= this.MS_BETWEEN_SENDS) &&
  586.         this.sendQueue.length > 0)                            
  587.     {
  588.         var s = this.sendQueue.pop();  
  589.       
  590.         if (s)
  591.         {
  592.             //dd ("queued send: " + s);
  593.             this.connection.sendData (s);
  594.             this.lastSend = d;
  595.         }
  596.  
  597.     }
  598.     else
  599.         this.parent.eventPump.addEvent (new CEvent ("event-pump", "yield",
  600.                                                     null, ""));
  601.  
  602.     if (this.sendQueue.length > 0)
  603.         this.parent.eventPump.addEvent (new CEvent ("server", "senddata",
  604.                                                     this, "onSendData"));
  605.     return true;
  606. }
  607.  
  608. CIRCServer.prototype.onPoll = 
  609. function serv_poll(e)
  610. {
  611.     var lines;
  612.     var ex;
  613.     var ev;
  614.     
  615.     try
  616.     {
  617.         line = this.connection.readData(this.READ_TIMEOUT);
  618.     }
  619.     catch (ex)
  620.     {
  621.         dd ("*** Caught exception " + ex + " reading from server " +
  622.             this.connection.host);
  623.         if (jsenv.HAS_RHINO && (ex instanceof java.lang.ThreadDeath))
  624.         {
  625.             dd("### catching a ThreadDeath");
  626.             throw(ex);
  627.         }
  628.         else
  629.         {
  630.             ev = new CEvent ("server", "disconnect", this, "onDisconnect");
  631.             ev.server = this;
  632.             ev.reason = "error";
  633.             ev.exception = ex;
  634.             this.parent.eventPump.addEvent (ev);
  635.             return false;
  636.         }
  637.     }
  638.         
  639.     this.parent.eventPump.addEvent (new CEvent ("server", "poll", this,
  640.                                                 "onPoll"));
  641.  
  642.     if (line)
  643.     {
  644.         ev = new CEvent ("server", "data-available", this, "onDataAvailable");
  645.         ev.line = line;
  646.         this.parent.eventPump.addEvent (ev);
  647.     }
  648.  
  649.     return true;
  650.     
  651. }
  652.  
  653. CIRCServer.prototype.onDataAvailable = 
  654. function serv_ppline(e)
  655. {
  656.     var line = e.line;
  657.     
  658.     if (line == "")
  659.         return false;
  660.     
  661.     var incomplete = (line[line.length] != '\n');
  662.     var lines = line.split("\n");
  663.  
  664.     if (this.savedLine)
  665.     {
  666.         lines[0] = this.savedLine + lines[0];
  667.         this.savedLine = "";
  668.     }
  669.     
  670.     if (incomplete)
  671.         this.savedLine = lines.pop();
  672.     
  673.     for (i in lines)
  674.     {
  675.         var ev = new CEvent("server", "rawdata", this, "onRawData");
  676.         ev.data = lines[i].replace(/\r/g, "");
  677.         this.parent.eventPump.addEvent (ev);
  678.     }
  679.  
  680.     return true;
  681. }
  682.  
  683. /*
  684.  * onRawData begins shaping the event by parsing the IRC message at it's
  685.  * simplest level.  After onRawData, the event will have the following
  686.  * properties:
  687.  * name           value
  688.  *
  689.  * set............"server"
  690.  * type..........."parsedata"
  691.  * destMethod....."onParsedData"
  692.  * destObject.....server (this)
  693.  * server.........server (this)
  694.  * connection.....CBSConnection (this.connection)
  695.  * source.........the <prefix> of the message (if it exists)
  696.  * user...........user object initialized with data from the message <prefix>
  697.  * params.........array containing the <middle> parameters of the message
  698.  * code...........the first <middle> parameter (most messages have this)
  699.  * meat...........the <trailing> parameter of the message
  700.  *
  701.  * See Section 2.3.1 of RFC 1459 for details on <prefix>, <middle> and
  702.  * <trailing> tokens.
  703.  */
  704. CIRCServer.prototype.onRawData = 
  705. function serv_onRawData(e)
  706. {
  707.     var ary;
  708.     var l = e.data;
  709.  
  710.     if (l[0] == ":")
  711.     {
  712.         ary = l.match (/:(\S+)\s(.*)/);
  713.         e.source = ary[1];
  714.         l = ary[2];
  715.         ary = e.source.match (/(\S+)!(\S+)@(.*)/);
  716.         if (ary)
  717.         {
  718.             e.user = new CIRCUser(this, ary[1], ary[2], ary[3]);
  719.         }
  720.         else
  721.         {
  722.             ary = e.source.match (/(\S+)@(.*)/);
  723.             if (ary)
  724.             {
  725.                 e.user = new CIRCUser(this, "", ary[1], ary[2]);
  726.             }
  727.         }
  728.     }
  729.  
  730.     e.server = this;
  731.  
  732.     var sep = l.indexOf(" :");
  733.  
  734.     if (sep != -1) /* <trailing> param, if there is one */
  735.         e.meat = l.substr (sep + 2, l.length);
  736.     else
  737.         e.meat = "";
  738.  
  739.     if (sep != -1)
  740.         e.params = l.substr(0, sep).split(" ");
  741.     else
  742.         e.params = l.split(" ");
  743.     e.code = e.params[0].toUpperCase();
  744.  
  745.     e.type = "parseddata";
  746.     e.destObject = this;
  747.     e.destMethod = "onParsedData";
  748.     
  749.     return true;
  750.     
  751. }
  752.  
  753. /*
  754.  * onParsedData forwards to next event, based on |e.code|
  755.  */
  756. CIRCServer.prototype.onParsedData = 
  757. function serv_onParsedData(e)
  758. {
  759.  
  760.     e.type = e.code.toLowerCase();
  761.     if (!e.code[0])
  762.     {
  763.         dd (dumpObjectTree (e));
  764.         return false;
  765.     }
  766.     
  767.     e.destMethod = "on" + e.code[0].toUpperCase() +
  768.         e.code.substr (1, e.code.length).toLowerCase();
  769.  
  770.     if (typeof this[e.destMethod] == "function")
  771.         e.destObject = this;
  772.     else if (typeof this["onUnknown"] == "function")
  773.         e.destMethod = "onUnknown";
  774.     else if (typeof this.parent[e.destMethod] == "function")
  775.     {
  776.         e.set = "network";
  777.         e.destObject = this.parent;
  778.     }
  779.     else
  780.     {
  781.         e.set = "network";
  782.         e.destObject = this.parent;
  783.         e.destMethod = "onUnknown";
  784.     }
  785.  
  786.     return true;
  787.     
  788. }
  789.  
  790. /* User changed topic */
  791. CIRCServer.prototype.onTopic = 
  792. function serv_topic (e)
  793. {
  794.  
  795.     e.channel = new CIRCChannel (this, e.params[1]);
  796.     e.channel.topicBy = e.user.nick;
  797.     e.channel.topicDate = new Date();
  798.     e.channel.topic = e.meat;
  799.     e.destObject = e.channel;
  800.     e.set = "channel";
  801.  
  802.     return true;
  803.     
  804. }
  805.  
  806. /* Successful login */
  807. CIRCServer.prototype.on001 =
  808. function serv_001 (e)
  809. {
  810.     this.parent.connectAttempt = 0;
  811.     delete this.parent.connecting;
  812.  
  813.     /* servers wont send a nick change notification if user was forced
  814.      * to change nick while logging in (eg. nick already in use.)  We need
  815.      * to verify here that what the server thinks our name is, matches what
  816.      * we think it is.  If not, the server wins.
  817.      */
  818.     if (e.params[1] != e.server.me.properNick)
  819.     {
  820.         renameProperty (e.server.users, e.server.me.nick, e.params[1]);
  821.         e.server.me.changeNick(e.params[1]);
  822.     }
  823.     
  824.     if (this.parent.INITIAL_CHANNEL)
  825.     {
  826.         this.parent.primChan = this.addChannel (this.parent.INITIAL_CHANNEL);
  827.         this.parent.primChan.join();
  828.     }
  829.  
  830.     this.parent.users = this.users;
  831.     e.destObject = this.parent;
  832.     e.set = "network";
  833. }
  834.  
  835.  
  836. /* TOPIC reply */
  837. CIRCServer.prototype.on332 =
  838. function serv_332 (e)
  839. {
  840.  
  841.     e.channel = new CIRCChannel (this, e.params[2]);
  842.     e.channel.topic = e.meat;
  843.     e.destObject = e.channel;
  844.     e.set = "channel";
  845.  
  846.     return true;
  847.     
  848. }
  849.  
  850. /* whois name */
  851. CIRCServer.prototype.on311 =
  852. function serv_311 (e)
  853. {
  854.     e.user = new CIRCUser (this, e.params[2], e.params[3], e.params[4]);
  855.     e.destObject = this.parent;
  856.     e.set = "network";
  857. }
  858.     
  859. /* whois server */
  860. CIRCServer.prototype.on312 =
  861. function serv_312 (e)
  862. {
  863.     e.user = new CIRCUser (this, e.params[2]);
  864.     e.user.connectionHost = e.params[3];
  865.  
  866.     e.destObject = this.parent;
  867.     e.set = "network";
  868. }
  869.  
  870. /* whois idle time */
  871. CIRCServer.prototype.on317 =
  872. function serv_317 (e)
  873. {
  874.     e.user = new CIRCUser (this, e.params[2]);
  875.     e.user.idleSeconds = e.params[3];
  876.  
  877.     e.destObject = this.parent;
  878.     e.set = "network";
  879. }
  880.  
  881. /* topic information */
  882. CIRCServer.prototype.on333 = 
  883. function serv_333 (e)
  884. {
  885.  
  886.     e.channel = new CIRCChannel (this, e.params[2]);
  887.     e.channel.topicBy = e.params[3];
  888.     e.channel.topicDate = new Date(Number(e.params[4]) * 1000);
  889.     e.destObject = e.channel;
  890.     e.set = "channel";
  891.  
  892.     return true;
  893.  
  894. }
  895.  
  896. /* who reply */
  897. CIRCServer.prototype.on352 = 
  898. function serv_352 (e)
  899. {
  900.     e.user = new CIRCUser (this, e.params[6], e.params[3], e.params[4]);
  901.     e.user.connectionHost = e.params[5];
  902.     e.destObject = this.parent;
  903.     e.set = "network";
  904.  
  905.     return true;
  906. }
  907.  
  908. /* end of who */
  909. CIRCServer.prototype.on315 = 
  910. function serv_315 (e)
  911. {
  912.  
  913.     e.user = new CIRCUser (this, e.params[2]);
  914.     e.destObject = this.parent;
  915.     e.set = "network";
  916.  
  917.     return true;
  918. }
  919.  
  920. /* name reply */
  921. CIRCServer.prototype.on353 = 
  922. function serv_353 (e)
  923. {
  924.     
  925.     e.channel = new CIRCChannel (this, e.params[3]);
  926.     if (e.channel.usersStable)
  927.     {        
  928.         e.channel.users = new Object();
  929.         e.channel.usersStable = false;
  930.     }
  931.     
  932.     e.destObject = e.channel;
  933.     e.set = "channel";
  934.  
  935.     var nicks = e.meat.split (" ");
  936.  
  937.     for (var n in nicks)
  938.     {
  939.         var nick = nicks[n];
  940.         if (nick == "")
  941.             break;
  942.         
  943.         switch (nick[0])
  944.         {
  945.             case "@":
  946.                 e.user = new CIRCChanUser (e.channel,
  947.                                            nick.substr(1, nick.length),
  948.                                            true, (void 0));
  949.                 break;
  950.             
  951.             case "+":
  952.                 e.user = new CIRCChanUser (e.channel,
  953.                                            nick.substr(1, nick.length),
  954.                                            (void 0), true);
  955.                 break;
  956.             
  957.             default:
  958.                 e.user = new CIRCChanUser (e.channel, nick);
  959.                 break;
  960.         }
  961.  
  962.     }
  963.  
  964.     return true;
  965.     
  966. }
  967.  
  968. /* end of names */
  969. CIRCServer.prototype.on366 = 
  970. function serv_366 (e)
  971. {
  972.  
  973.     e.channel = new CIRCChannel (this, e.params[2]);
  974.     e.destObject = e.channel;
  975.     e.set = "channel";
  976.     e.channel.usersStable = true;
  977.  
  978.     return true;
  979.     
  980. }    
  981.  
  982. /* channel time stamp? */
  983. CIRCServer.prototype.on329 = 
  984. function serv_329 (e)
  985. {
  986.  
  987.     e.channel = new CIRCChannel (this, e.params[2]);
  988.     e.destObject = e.channel;
  989.     e.set = "channel";
  990.     e.channel.timeStamp = new Date (Number(e.params[3]) * 1000);
  991.     
  992.     return true;
  993.     
  994. }
  995.     
  996. /* channel mode reply */
  997. CIRCServer.prototype.on324 = 
  998. function serv_324 (e)
  999. {
  1000.  
  1001.     e.channel = new CIRCChannel (this, e.params[2]);
  1002.     e.destObject = this;
  1003.     e.type = "chanmode";
  1004.     e.destMethod = "onChanMode";
  1005.  
  1006.     return true;
  1007.  
  1008. }
  1009.  
  1010. /* user changed the mode */
  1011. CIRCServer.prototype.onMode = 
  1012. function serv_mode (e)
  1013. {
  1014.  
  1015.     e.destObject = this;
  1016.     
  1017.     if ((e.params[1][0] == "#") || (e.params[1][0] == "&"))
  1018.     {
  1019.         e.channel = new CIRCChannel (this, e.params[1]);
  1020.         if ("user" in e && e.user)
  1021.             e.user = new CIRCChanUser (e.channel, e.user.nick);
  1022.         e.type = "chanmode";
  1023.         e.destMethod = "onChanMode";
  1024.     }
  1025.     else
  1026.     {
  1027.         e.type = "usermode";
  1028.         e.destMethod = "onUserMode";
  1029.     }
  1030.     
  1031.     return true;
  1032.     
  1033. }
  1034.  
  1035. CIRCServer.prototype.onUserMode = 
  1036. function serv_usermode (e)
  1037. {
  1038.  
  1039.     return true;
  1040.     
  1041. }
  1042.  
  1043. CIRCServer.prototype.onChanMode = 
  1044. function serv_chanmode (e)
  1045. {
  1046.     var modifier = "";
  1047.     var params_eaten = 0;
  1048.     var BASE_PARAM;
  1049.  
  1050.     if (e.code.toUpperCase() == "MODE")
  1051.         BASE_PARAM = 2;
  1052.     else
  1053.         if (e.code == "324")
  1054.             BASE_PARAM = 3;
  1055.         else
  1056.         {
  1057.             dd ("** INVALID CODE in ChanMode event **");
  1058.             return false;
  1059.         }
  1060.  
  1061.     var mode_str = e.params[BASE_PARAM];
  1062.     params_eaten++;
  1063.  
  1064.     e.modeStr = mode_str;
  1065.     e.usersAffected = new Array();
  1066.  
  1067.     var nick;
  1068.     var user;
  1069.     
  1070.     for (var i = 0; i < mode_str.length ; i++)
  1071.     {
  1072.         switch (mode_str[i])
  1073.         {
  1074.             case "+":
  1075.             case "-":
  1076.                 modifier = mode_str[i];
  1077.                 break;
  1078.  
  1079.             /* user modes */
  1080.             case "o": /* operator */
  1081.                 if (modifier == "+")
  1082.                 {
  1083.                     nick = e.params[BASE_PARAM + params_eaten];
  1084.                     user = new CIRCChanUser (e.channel, nick, true);
  1085.                     params_eaten++;
  1086.                     e.usersAffected.push (user);
  1087.                 }
  1088.                 else
  1089.                     if (modifier == "-")
  1090.                     {
  1091.                         nick = e.params[BASE_PARAM + params_eaten];
  1092.                         user = new CIRCChanUser (e.channel, nick, false);
  1093.                         params_eaten++;
  1094.                         e.usersAffected.push (user);
  1095.                     }
  1096.                 break;
  1097.                         
  1098.             case "v": /* voice */
  1099.                 if (modifier == "+")
  1100.                 {
  1101.                     nick = e.params[BASE_PARAM + params_eaten];
  1102.                     user = new CIRCChanUser (e.channel, nick, (void 0), true);
  1103.                     params_eaten++;
  1104.                     e.usersAffected.push (user);
  1105.                 }
  1106.                 else
  1107.                     if (modifier == "-")
  1108.                     {
  1109.                         nick = e.params[BASE_PARAM + params_eaten];
  1110.                         user = new CIRCChanUser (e.channel, nick, (void 0),
  1111.                                                  false);
  1112.                         params_eaten++;
  1113.                         e.usersAffected.push (user);
  1114.                     }
  1115.                 break;
  1116.                 
  1117.             case "b": /* ban */
  1118.                 var ban = e.params[BASE_PARAM + params_eaten];
  1119.                 params_eaten++;
  1120.  
  1121.                 if ((modifier == "+") &&
  1122.                     (typeof e.channel.bans[ban] == "undefined"))
  1123.                 {
  1124.                     e.channel.bans[ban] = {host: ban};
  1125.                     var ban_evt = new CEvent ("channel", "ban", e.channel,
  1126.                                           "onBan");
  1127.                     ban_evt.channel = e.channel;
  1128.                     ban_evt.ban = ban;
  1129.                     ban_evt.source = e.user;
  1130.                     this.parent.eventPump.addEvent (e);
  1131.                 }
  1132.                 else
  1133.                     if (modifier == "-")
  1134.                         delete e.channel.bans[ban];
  1135.                 break;
  1136.  
  1137.                 
  1138.             /* channel modes */
  1139.             case "l": /* limit */
  1140.                 if (modifier == "+")
  1141.                 {
  1142.                     var limit = e.params[BASE_PARAM + params_eaten];
  1143.                     params_eaten++;
  1144.                     e.channel.mode.limit = limit;
  1145.                 }
  1146.                 else
  1147.                     if (modifier == "-")
  1148.                         e.channel.mode.limit = -1;
  1149.                 break;
  1150.  
  1151.             case "k": /* key */
  1152.                 var key = e.params[BASE_PARAM + params_eaten];
  1153.                 params_eaten++;
  1154.  
  1155.                 if (modifier == "+")
  1156.                     e.channel.mode.key = key;
  1157.                 else
  1158.                     if (modifier == "-")
  1159.                         e.channel.mode.key = "";
  1160.                 break;
  1161.  
  1162.             case "m": /* moderated */
  1163.                 if (modifier == "+")
  1164.                     e.channel.mode.moderated = true;
  1165.                 else
  1166.                     if (modifier == "-")
  1167.                         e.channel.mode.moderated = false;
  1168.                 break;
  1169.  
  1170.             case "n": /* no outside messages */
  1171.                 if (modifier == "+")
  1172.                     e.channel.mode.publicMessages = false;
  1173.                 else
  1174.                     if (modifier == "-")
  1175.                         e.channel.mode.publicMessages = true;
  1176.                 break;
  1177.  
  1178.             case "t": /* topic */
  1179.                 if (modifier == "+")
  1180.                     e.channel.mode.publicTopic = false;
  1181.                 else
  1182.                     if (modifier == "-")
  1183.                         e.channel.mode.publicTopic = true;
  1184.                 break;
  1185.                 
  1186.             case "i": /* invite */
  1187.                 if (modifier == "+")
  1188.                     e.channel.mode.invite = true;
  1189.                 else
  1190.                     if (modifier == "-")
  1191.                         e.channel.mode.invite = false;
  1192.                 break;
  1193.  
  1194.             case "s": /* secret */
  1195.                 if (modifier == "+")
  1196.                     e.channel.mode.secret  = true;
  1197.                 else
  1198.                     if (modifier == "-")
  1199.                         e.channel.mode.secret = false;
  1200.                 break;
  1201.                 
  1202.             case "p": /* private */
  1203.                 if (modifier == "+")
  1204.                     e.channel.mode.pvt = true;
  1205.                 else
  1206.                     if (modifier == "-")
  1207.                         e.channel.mode.pvt = false;
  1208.                 break;
  1209.                 
  1210.         }
  1211.     }
  1212.  
  1213.     e.destObject = e.channel;
  1214.     e.set = "channel";
  1215.     return true;
  1216.  
  1217. }
  1218.  
  1219. CIRCServer.prototype.onNick = 
  1220. function serv_nick (e)
  1221. {
  1222.     /* Some irc networks send the new nick in the meat, some send it in param[1]
  1223.      * Handle both cases. */
  1224.     var newNick = (e.meat) ? e.meat : e.params[1]; 
  1225.     var newKey = newNick.toLowerCase();
  1226.     var oldKey = e.user.nick;
  1227.     var ev;
  1228.     
  1229.     renameProperty (this.users, oldKey, newKey);
  1230.     e.oldNick = e.user.properNick;
  1231.     e.user.changeNick(newNick);
  1232.     
  1233.     for (var c in this.channels)
  1234.     {
  1235.         if (oldKey in this.channels[c].users)
  1236.         {
  1237.             var cuser = this.channels[c].users[oldKey];
  1238.             renameProperty (this.channels[c].users, oldKey, newKey);
  1239.             ev = new CEvent ("channel", "nick", this.channels[c],
  1240.                                  "onNick");
  1241.             ev.channel = this.channels[c];
  1242.             ev.user = cuser;
  1243.             ev.server = this;
  1244.             ev.oldNick = e.oldNick;
  1245.             this.parent.eventPump.addEvent(ev);
  1246.         }
  1247.         
  1248.     }
  1249.  
  1250.     if (e.user == this.me)
  1251.     {
  1252.         /* if it was me, tell the network about the nick change as well */
  1253.         ev = new CEvent ("network", "nick", this.parent, "onNick");
  1254.         ev.user = e.user;
  1255.         ev.server = this;
  1256.         ev.oldNick = e.oldNick;
  1257.         this.parent.eventPump.addEvent(ev);
  1258.     }
  1259.  
  1260.     e.destObject = e.user;
  1261.     e.set = "user";    
  1262.  
  1263.     return true;
  1264.     
  1265. }
  1266.  
  1267. CIRCServer.prototype.onQuit = 
  1268. function serv_quit (e)
  1269. {
  1270.  
  1271.     for (var c in e.server.channels)
  1272.     {
  1273.         if (e.server.channels[c].active &&
  1274.             e.user.nick in e.server.channels[c].users)
  1275.         {
  1276.             var ev = new CEvent ("channel", "quit", e.server.channels[c],
  1277.                                  "onQuit");
  1278.             ev.user = e.server.channels[c].users[e.user.nick];
  1279.             ev.channel = e.server.channels[c];
  1280.             ev.server = ev.channel.parent;
  1281.             ev.reason = e.meat;
  1282.             this.parent.eventPump.addEvent(ev);
  1283.             delete e.server.channels[c].users[e.user.nick];
  1284.         }
  1285.     }
  1286.  
  1287.     this.users[e.user.nick].lastQuitMessage = e.meat;
  1288.     this.users[e.user.nick].lastQuitDate = new Date;
  1289.  
  1290.     e.reason = e.meat;
  1291.     e.destObject = e.user;
  1292.     e.set = "user";
  1293.  
  1294.     return true;
  1295.  
  1296. }
  1297.  
  1298. CIRCServer.prototype.onPart = 
  1299. function serv_part (e)
  1300. {
  1301.  
  1302.     e.channel = new CIRCChannel (this, e.params[1]);    
  1303.     e.user = new CIRCChanUser (e.channel, e.user.nick);
  1304.     if (userIsMe(e.user))
  1305.         e.channel.active = false;
  1306.     e.channel.removeUser(e.user.nick);
  1307.     e.destObject = e.channel;
  1308.     e.set = "channel";
  1309.  
  1310.     return true;
  1311.     
  1312. }
  1313.  
  1314. CIRCServer.prototype.onKick = 
  1315. function serv_kick (e)
  1316. {
  1317.  
  1318.     e.channel = new CIRCChannel (this, e.params[1]);
  1319.     e.lamer = new CIRCChanUser (e.channel, e.params[2]);
  1320.     delete e.channel.users[e.lamer.nick];
  1321.     if (userIsMe(e.lamer))
  1322.         e.channel.active = false;
  1323.     e.reason = e.meat;
  1324.     e.destObject = e.channel;
  1325.     e.set = "channel"; 
  1326.  
  1327.     return true;
  1328.     
  1329. }
  1330.  
  1331. CIRCServer.prototype.onJoin = 
  1332. function serv_join (e)
  1333. {
  1334.  
  1335.     e.channel = new CIRCChannel (this, e.meat);
  1336.     if (e.user == this.me)
  1337.         e.server.sendData ("MODE " + e.channel.name + "\n" /* +
  1338.                            "BANS " + e.channel.name + "\n" */);
  1339.     e.user = new CIRCChanUser (e.channel, e.user.nick);
  1340.     if (userIsMe(e.user))
  1341.         e.channel.active = true;
  1342.  
  1343.     e.destObject = e.channel;
  1344.     e.set = "channel";
  1345.  
  1346.     return true;
  1347.     
  1348. }
  1349.  
  1350. CIRCServer.prototype.onPing = 
  1351. function serv_ping (e)
  1352. {
  1353.  
  1354.     /* non-queued send, so we can calcualte lag */
  1355.     this.connection.sendData ("PONG :" + e.meat + "\n");
  1356.     this.connection.sendData ("PING :LAGTIMER\n");
  1357.     this.lastPing = this.lastPingSent = new Date();
  1358.  
  1359.     e.destObject = this.parent;
  1360.     e.set = "network";
  1361.  
  1362.     return true;
  1363.     
  1364. }
  1365.  
  1366. CIRCServer.prototype.onPong = 
  1367. function serv_pong (e)
  1368. {
  1369.     if (e.meat != "LAGTIMER")
  1370.         return true;
  1371.     
  1372.     if (this.lastPingSent)
  1373.         this.lag = roundTo ((new Date() - this.lastPingSent) / 1000, 1);
  1374.  
  1375.     delete this.lastPingSent;
  1376.     
  1377.     e.destObject = this.parent;
  1378.     e.set = "network";
  1379.  
  1380.     return true;
  1381.     
  1382. }
  1383.  
  1384. CIRCServer.prototype.onNotice = 
  1385. function serv_notice (e)
  1386. {
  1387.  
  1388.     if (!("user" in e))
  1389.     {
  1390.         e.set = "network";
  1391.         e.destObject = this.parent;
  1392.         return true;
  1393.     }
  1394.         
  1395.     if ((e.params[1][0] == "#") || (e.params[1][0] == "&"))
  1396.     {
  1397.         e.channel = new CIRCChannel(this, e.params[1]);
  1398.         e.user = new CIRCChanUser (e.channel, e.user.nick);
  1399.         e.replyTo = e.channel;
  1400.         e.set = "channel";
  1401.     }
  1402.     else if (e.meat.search (/\x01.*\x01/i) != -1)
  1403.     {
  1404.         e.type = "ctcp-reply";
  1405.         e.destMethod = "onCTCPReply";
  1406.         e.set = "server";
  1407.         e.destObject = this;
  1408.         return true;
  1409.     }
  1410.     else
  1411.     {
  1412.         e.set = "user";
  1413.         e.replyTo = e.user; /* send replys to the user who sent the message */
  1414.     }
  1415.  
  1416.     e.destObject = e.replyTo;
  1417.  
  1418.     return true;
  1419.     
  1420. }
  1421.  
  1422. CIRCServer.prototype.onPrivmsg = 
  1423. function serv_privmsg (e)
  1424. {
  1425.     
  1426.     /* setting replyTo provides a standard place to find the target for     */
  1427.     /* replys associated with this event.                                   */
  1428.     if ((e.params[1][0] == "#") || (e.params[1][0] == "&"))
  1429.     {
  1430.         e.channel = new CIRCChannel(this, e.params[1]);
  1431.         e.user = new CIRCChanUser (e.channel, e.user.nick);
  1432.         e.replyTo = e.channel;
  1433.         e.set = "channel";
  1434.     }
  1435.     else
  1436.     {
  1437.         e.set = "user";
  1438.         e.replyTo = e.user; /* send replys to the user who sent the message */
  1439.     }
  1440.  
  1441.     if (e.meat.search (/\x01.*\x01/i) != -1)
  1442.     {
  1443.         e.type = "ctcp";
  1444.         e.destMethod = "onCTCP";
  1445.         e.set = "server";
  1446.         e.destObject = this;
  1447.     }
  1448.     else
  1449.         e.destObject = e.replyTo;
  1450.  
  1451.     return true;
  1452.     
  1453. }
  1454.  
  1455. CIRCServer.prototype.onCTCPReply = 
  1456. function serv_ctcpr (e)
  1457. {
  1458.     var ary = e.meat.match (/^\x01(\S+) ?(.*)\x01$/i);
  1459.  
  1460.     if (ary == null)
  1461.         return false;
  1462.  
  1463.     e.CTCPData = ary[2] ? ary[2] : "";
  1464.  
  1465.     e.CTCPCode = ary[1].toLowerCase();
  1466.     e.type = "ctcp-reply-" + e.CTCPCode;
  1467.     e.destMethod = "onCTCPReply" + ary[1][0].toUpperCase() +
  1468.         ary[1].substr (1, ary[1].length).toLowerCase();
  1469.  
  1470.     if (typeof this[e.destMethod] != "function")
  1471.     { /* if there's no place to land the event here, try to forward it */
  1472.         e.destObject = this.parent;
  1473.         e.set = "network";
  1474.         
  1475.         if (typeof e.destObject[e.destMethod] != "function")
  1476.         { /* if there's no place to forward it, send it to unknownCTCP */
  1477.             e.type = "unk-ctcp-reply";
  1478.             e.destMethod = "onUnknownCTCPReply";
  1479.             if (e.destMethod in this)
  1480.             {
  1481.                 e.set = "server";
  1482.                 e.destObject = this;
  1483.             }
  1484.             else
  1485.             {
  1486.                 e.set = "network";
  1487.                 e.destObject = this.parent;
  1488.             }
  1489.         }
  1490.     }
  1491.     else
  1492.         e.destObject = this;
  1493.  
  1494.     return true;
  1495.     
  1496. }
  1497.  
  1498. CIRCServer.prototype.onCTCP = 
  1499. function serv_ctcp (e)
  1500. {
  1501.     var ary = e.meat.match (/^\x01(\S+) ?(.*)\x01$/i);
  1502.  
  1503.     if (ary == null)
  1504.         return false;
  1505.  
  1506.     e.CTCPData = ary[2] ? ary[2] : "";
  1507.  
  1508.     e.CTCPCode = ary[1].toLowerCase();
  1509.     e.type = "ctcp-" + e.CTCPCode;
  1510.     e.destMethod = "onCTCP" + ary[1][0].toUpperCase() +
  1511.         ary[1].substr (1, ary[1].length).toLowerCase();
  1512.  
  1513.     if (typeof this[e.destMethod] != "function")
  1514.     { /* if there's no place to land the event here, try to forward it */
  1515.         e.destObject = e.replyTo;
  1516.         e.set = (e.replyTo == e.user) ? "user" : "channel";
  1517.         
  1518.         if (typeof e.replyTo[e.destMethod] != "function")
  1519.         { /* if there's no place to forward it, send it to unknownCTCP */
  1520.             e.type = "unk-ctcp";
  1521.             e.destMethod = "onUnknownCTCP";
  1522.         }
  1523.     }
  1524.     else
  1525.         e.destObject = this;
  1526.  
  1527.     return true;
  1528.     
  1529. }
  1530.  
  1531. CIRCServer.prototype.onCTCPAction =
  1532. function serv_cact (e)
  1533. {
  1534.     e.destObject = e.replyTo;
  1535.     e.set = (e.replyTo == e.user) ? "user" : "channel";        
  1536.  
  1537. }
  1538.  
  1539.  
  1540. CIRCServer.prototype.onCTCPVersion = 
  1541. function serv_cver (e)
  1542. {
  1543.     var lines = e.server.VERSION_RPLY.split ("\n");
  1544.     
  1545.     for (var i in lines)
  1546.         e.user.notice ("\01VERSION " + lines[i] + "\01");
  1547.     
  1548.     e.destObject = e.replyTo;
  1549.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1550.  
  1551.     return true;
  1552.     
  1553. }
  1554.  
  1555. CIRCServer.prototype.onCTCPPing = 
  1556. function serv_cping (e)
  1557. {
  1558.  
  1559.     /* non-queued send */
  1560.     e.server.connection.sendData ("NOTICE " + e.user.nick + " :\01PING " +
  1561.                                   e.CTCPData + "\01\n");
  1562.     e.destObject = e.replyTo;
  1563.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1564.  
  1565.     return true;
  1566.     
  1567. }
  1568.  
  1569. CIRCServer.prototype.onCTCPDcc = 
  1570. function serv_dcc (e)
  1571. {
  1572.  
  1573.     var ary = e.CTCPData.match (/(\S+)? ?(.*)/);
  1574.     
  1575.     e.DCCData = ary[2];
  1576.     e.type = "dcc-" + ary[1].toLowerCase();
  1577.     e.destMethod = "onDCC" + ary[1][0].toUpperCase() +
  1578.         ary[1].substr (1, ary[1].length).toLowerCase();
  1579.  
  1580.     if (typeof this[e.destMethod] != "function")
  1581.     { /* if there's no place to land the event here, try to forward it */
  1582.         e.destObject = e.replyTo;
  1583.         e.set = (e.replyTo == e.user) ? "user" : "channel";
  1584.     }
  1585.     else
  1586.         e.destObject = this;
  1587.  
  1588.     return true;
  1589.  
  1590. }
  1591.  
  1592. CIRCServer.prototype.onDCCChat = 
  1593. function serv_dccchat (e)
  1594. {
  1595.     var ary = e.DCCData.match (/(chat) (\d+) (\d+)/i);
  1596.  
  1597.     if (ary == null)
  1598.         return false;
  1599.  
  1600.     e.id = ary[2];
  1601.     e.port = ary[3];
  1602.     e.destObject = e.replyTo;
  1603.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1604.     
  1605.     return true;
  1606.     
  1607. }
  1608.  
  1609. CIRCServer.prototype.onDCCSend = 
  1610. function serv_dccsend (e)
  1611. {
  1612.  
  1613.     var ary = e.DCCData.match (/(\S+) (\d+) (\d+) (\d+)/);
  1614.  
  1615.     if (ary == null)
  1616.         return false;
  1617.     
  1618.     e.file = ary[1];
  1619.     e.id   = ary[2];
  1620.     e.port = ary[3];
  1621.     e.size = ary[4];
  1622.     e.destObject = e.replyTo;
  1623.     e.set = (e.replyTo == e.user) ? "user" : "channel";
  1624.  
  1625.     return true;
  1626.     
  1627. }
  1628.  
  1629. /*
  1630.  * channel
  1631.  */
  1632.  
  1633. function CIRCChannel (parent, name)
  1634. {
  1635.  
  1636.     name = name.toLowerCase();
  1637.     
  1638.     if (name in parent.channels)
  1639.         return parent.channels[name];
  1640.     
  1641.     this.parent = parent;
  1642.     this.name = name;
  1643.     this.users = new Object();
  1644.     this.bans = new Object();
  1645.     this.mode = new CIRCChanMode (this);
  1646.     this.usersStable = true;
  1647.     
  1648.     parent.channels[name] = this;
  1649.  
  1650.     return this;
  1651.     
  1652. }
  1653.  
  1654. CIRCChannel.prototype.TYPE = "IRCChannel";
  1655. CIRCChannel.prototype.topic = "";
  1656.  
  1657. CIRCChannel.prototype.getURL =
  1658. function chan_geturl ()
  1659. {
  1660.     var target;
  1661.     if (this.name[0] == "#")
  1662.         target = this.name.substr(1);
  1663.     else
  1664.         target = escape(this.name);
  1665.     return this.parent.parent.getURL() + target;
  1666. }
  1667.  
  1668. CIRCChannel.prototype.addUser = 
  1669. function chan_adduser (nick, isOp, isVoice)
  1670. {
  1671.  
  1672.     return new CIRCChanUser (this, nick, isOp, isVoice);
  1673.     
  1674. }
  1675.  
  1676. CIRCChannel.prototype.getUser =
  1677. function chan_getuser (nick) 
  1678. {
  1679.     
  1680.     nick = nick.toLowerCase(); // assumes valid param!
  1681.     var cuser = this.users[nick];
  1682.     return cuser; // caller expected to check for undefinededness    
  1683.  
  1684. }
  1685.  
  1686. CIRCChannel.prototype.removeUser =
  1687. function chan_removeuser (nick)
  1688. {
  1689.     delete this.users[nick.toLowerCase()]; // see ya
  1690. }
  1691.  
  1692. CIRCChannel.prototype.getUsersLength = 
  1693. function chan_userslen ()
  1694. {
  1695.     var i = 0;
  1696.     this.opCount = 0;
  1697.     this.voiceCount = 0;
  1698.     
  1699.     for (var p in this.users)
  1700.     {
  1701.         if (this.users[p].isOp)
  1702.             ++this.opCount;
  1703.         else if (this.users[p].isVoice)
  1704.             ++this.voiceCount;
  1705.         i++;
  1706.     }
  1707.  
  1708.     return i;
  1709.     
  1710. }
  1711.  
  1712. CIRCChannel.prototype.setTopic = 
  1713. function chan_topic (str)
  1714. {
  1715.     if ((!this.mode.publicTopic) && 
  1716.         (!this.users[this.parent.me.nick].isOp))
  1717.         return false;
  1718.     
  1719.     str = String(str).split("\n");
  1720.     for (var i in str)
  1721.         this.parent.sendData ("TOPIC " + this.name + " :" + str[i] + "\n");
  1722.     
  1723.     return true;
  1724.  
  1725. }
  1726.  
  1727. CIRCChannel.prototype.say = 
  1728. function chan_say (msg)
  1729. {
  1730.  
  1731.     this.parent.sayTo (this.name, msg);
  1732.     
  1733. }
  1734.  
  1735. CIRCChannel.prototype.act = 
  1736. function chan_say (msg)
  1737. {
  1738.  
  1739.     this.parent.actTo (this.name, msg);
  1740.     
  1741. }
  1742.  
  1743. CIRCChannel.prototype.notice = 
  1744. function chan_notice (msg)
  1745. {
  1746.  
  1747.     this.parent.noticeTo (this.name, msg);
  1748.     
  1749. }
  1750.  
  1751. CIRCChannel.prototype.ctcp = 
  1752. function chan_ctcpto (code, msg, type)
  1753. {
  1754.     if (typeof msg == "undefined")
  1755.         msg = "";
  1756.  
  1757.     if (typeof type == "undefined")
  1758.         type = "PRIVMSG";
  1759.     
  1760.      
  1761.     this.parent.messageTo (type, this.name, msg, code);
  1762.  
  1763. }
  1764.  
  1765. CIRCChannel.prototype.join = 
  1766. function chan_join (key)
  1767. {
  1768.     if (!key)
  1769.         key = "";
  1770.     
  1771.     this.parent.sendData ("JOIN " + this.name + " " + key + "\n");
  1772.     return true;
  1773.     
  1774. }
  1775.  
  1776. CIRCChannel.prototype.part = 
  1777. function chan_part ()
  1778. {
  1779.     
  1780.     this.parent.sendData ("PART " + this.name + "\n");
  1781.     this.users = new Object();
  1782.     return true;
  1783.     
  1784. }
  1785.  
  1786. /**
  1787.  * Invites a user to a channel.
  1788.  *
  1789.  * @param nick  the user name to invite.
  1790.  */
  1791. CIRCChannel.prototype.invite =
  1792. function chan_inviteuser (nick)
  1793. {
  1794.  
  1795.     this.parent.sendData("INVITE " + nick + " " + this.name + "\n");
  1796.     return true;
  1797.  
  1798. }
  1799.  
  1800. /*
  1801.  * channel mode
  1802.  */
  1803. function CIRCChanMode (parent)
  1804. {
  1805.  
  1806.     this.parent = parent;
  1807.     this.limit = -1;
  1808.     this.key = "";
  1809.     this.moderated = false;
  1810.     this.publicMessages = true;
  1811.     this.publicTopic = true;
  1812.     this.invite = false;
  1813.     this.secret = false;
  1814.     this.pvt = false;
  1815.     
  1816. }
  1817.  
  1818. CIRCChanMode.prototype.TYPE = "IRCChanMode";
  1819.  
  1820. CIRCChanMode.prototype.getModeStr = 
  1821. function chan_modestr (f)
  1822. {
  1823.     var str = "";
  1824.  
  1825.     if (this.invite)
  1826.         str += "i";
  1827.     if (this.moderated)
  1828.         str += "m";
  1829.     if (!this.publicMessages)
  1830.         str += "n";
  1831.     if (!this.publicTopic)
  1832.         str += "t";
  1833.     if (this.secret)
  1834.         str += "s";
  1835.     if (this.pvt)
  1836.         str += "p";
  1837.     if (this.key)
  1838.         str += "k";
  1839.     if (this.limit != -1)
  1840.         str += "l " + this.limit;
  1841.     
  1842.     if (str)
  1843.         str = "+" + str;
  1844.  
  1845.     return str;
  1846.     
  1847. }
  1848.  
  1849. CIRCChanMode.prototype.setMode = 
  1850. function chanm_mode (modestr)
  1851. {
  1852.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1853.         return false;
  1854.  
  1855.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1856.                                  modestr + "\n");
  1857.  
  1858.     return true;
  1859.     
  1860. }
  1861.  
  1862. CIRCChanMode.prototype.setLimit = 
  1863. function chanm_limit (n)
  1864. {
  1865.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1866.         return false;
  1867.  
  1868.     if ((typeof n == "undefined") || (n <= 0))
  1869.         this.parent.parent.sendData ("MODE " + this.parent.name + " -l\n");
  1870.     else
  1871.         this.parent.parent.sendData ("MODE " + this.parent.name + " +l " +
  1872.                                      Number(n) + "\n");
  1873.  
  1874.     return true;
  1875.     
  1876. }
  1877.  
  1878. CIRCChanMode.prototype.lock = 
  1879. function chanm_lock (k)
  1880. {
  1881.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1882.         return false;
  1883.     
  1884.     this.parent.parent.sendData ("MODE " + this.parent.name + " +k " +
  1885.                                  k + "\n");
  1886.     return true;
  1887.     
  1888. }
  1889.  
  1890. CIRCChanMode.prototype.unlock = 
  1891. function chan_unlock (k)
  1892. {
  1893.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1894.         return false;
  1895.     
  1896.     this.parent.parent.sendData ("MODE " + this.parent.name + " -k " +
  1897.                                  k + "\n");
  1898.     return true;
  1899.     
  1900. }
  1901.  
  1902. CIRCChanMode.prototype.setModerated = 
  1903. function chan_moderate (f)
  1904. {
  1905.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1906.         return false;
  1907.  
  1908.     var modifier = (f) ? "+" : "-";
  1909.     
  1910.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1911.                                  modifier + "m\n");
  1912.     return true;
  1913.     
  1914. }
  1915.  
  1916. CIRCChanMode.prototype.setPublicMessages = 
  1917. function chan_pmessages (f)
  1918. {
  1919.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1920.         return false;
  1921.  
  1922.     var modifier = (f) ? "-" : "+";
  1923.     
  1924.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1925.                                  modifier + "n\n");
  1926.     return true;
  1927.     
  1928. }
  1929.  
  1930. CIRCChanMode.prototype.setPublicTopic = 
  1931. function chan_ptopic (f)
  1932. {
  1933.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1934.         return false;
  1935.  
  1936.     var modifier = (f) ? "-" : "+";
  1937.     
  1938.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1939.                                  modifier + "t\n");
  1940.     return true;
  1941.     
  1942. }
  1943.  
  1944. CIRCChanMode.prototype.setInvite = 
  1945. function chan_invite (f)
  1946. {
  1947.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1948.         return false;
  1949.  
  1950.     var modifier = (f) ? "+" : "-";
  1951.     
  1952.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1953.                                  modifier + "i\n");
  1954.     return true;
  1955.     
  1956. }
  1957.  
  1958. CIRCChanMode.prototype.setPvt = 
  1959. function chan_pvt (f)
  1960. {
  1961.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1962.         return false;
  1963.  
  1964.     var modifier = (f) ? "+" : "-";
  1965.     
  1966.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1967.                                  modifier + "p\n");
  1968.     return true;
  1969.     
  1970. }
  1971.  
  1972. CIRCChanMode.prototype.setSecret = 
  1973. function chan_secret (f)
  1974. {
  1975.     if (!this.parent.users[this.parent.parent.me.nick].isOp)
  1976.         return false;
  1977.  
  1978.     var modifier = (f) ? "+" : "-";
  1979.     
  1980.     this.parent.parent.sendData ("MODE " + this.parent.name + " " +
  1981.                                  modifier + "p\n");
  1982.     return true;
  1983.     
  1984. }
  1985.  
  1986. /*
  1987.  * user
  1988.  */
  1989.  
  1990. function CIRCUser (parent, nick, name, host)
  1991. {
  1992.     var properNick = nick;
  1993.     nick = nick.toLowerCase();
  1994.     
  1995.     if (nick in parent.users)
  1996.     {
  1997.         var existingUser = parent.users[nick];
  1998.         if (name) existingUser.name = name;
  1999.         if (host) existingUser.host = host;
  2000.         return existingUser;
  2001.     }
  2002.  
  2003.     this.parent = parent;
  2004.     this.nick = nick;
  2005.     this.properNick = properNick;
  2006.     this.name = name;
  2007.     this.host = host;
  2008.  
  2009.     parent.users[nick] = this;
  2010.  
  2011.     return this;
  2012.  
  2013. }
  2014.  
  2015. CIRCUser.prototype.TYPE = "IRCUser";
  2016.  
  2017. CIRCUser.prototype.getURL =
  2018. function usr_geturl ()
  2019. {
  2020.     return this.parent.getURL() + this.nick + ",isnick";
  2021. }
  2022.  
  2023. CIRCUser.prototype.changeNick =
  2024. function usr_changenick (nick)
  2025. {
  2026.  
  2027.     this.properNick = nick;
  2028.     this.nick = nick.toLowerCase();
  2029.     
  2030. }
  2031.  
  2032. CIRCUser.prototype.getHostMask = 
  2033. function usr_hostmask (pfx)
  2034. {
  2035.     pfx = (typeof pfx != "undefined") ? pfx : "*!" + this.name + "@*.";
  2036.     var idx = this.host.indexOf(".");
  2037.     if (idx == -1)
  2038.         return pfx + this.host;
  2039.     
  2040.     return (pfx + this.host.substr(idx + 1, this.host.length));
  2041.     
  2042. }
  2043.  
  2044. CIRCUser.prototype.say = 
  2045. function usr_say (msg)
  2046. {
  2047.  
  2048.     this.parent.sayTo (this.nick, msg);
  2049.     
  2050. }
  2051.  
  2052. CIRCUser.prototype.notice = 
  2053. function usr_notice (msg)
  2054. {
  2055.  
  2056.     this.parent.noticeTo (this.nick, msg);
  2057.     
  2058. }
  2059.  
  2060. CIRCUser.prototype.act = 
  2061. function usr_act (msg)
  2062. {
  2063.  
  2064.     this.parent.actTo (this.nick, msg);
  2065.     
  2066. }
  2067.  
  2068. CIRCUser.prototype.ctcp = 
  2069. function usr_ctcp (code, msg, type)
  2070. {
  2071.     if (typeof msg == "undefined")
  2072.         msg = "";
  2073.  
  2074.     if (typeof type == "undefined")
  2075.         type = "PRIVMSG";
  2076.     
  2077.      
  2078.     this.parent.messageTo (type, this.name, msg, code);
  2079.  
  2080. }
  2081.  
  2082. CIRCUser.prototype.whois =
  2083. function usr_whois ()
  2084. {
  2085.  
  2086.     this.parent.whois (this.nick);
  2087. }   
  2088.  
  2089.     
  2090. /*
  2091.  * channel user
  2092.  */
  2093. function CIRCChanUser (parent, nick, isOp, isVoice)
  2094. {
  2095.     var properNick = nick;
  2096.     nick = nick.toLowerCase();    
  2097.  
  2098.     if (nick in parent.users)
  2099.     {
  2100.         var existingUser = parent.users[nick];
  2101.         if (typeof isOp != "undefined") existingUser.isOp = isOp;
  2102.         if (typeof isVoice != "undefined") existingUser.isVoice = isVoice;
  2103.         return existingUser;
  2104.     }
  2105.         
  2106.     var protoUser = new CIRCUser (parent.parent, properNick);
  2107.         
  2108.     this.__proto__ = protoUser;
  2109.     this.getURL = cusr_geturl;
  2110.     this.setOp = cusr_setop;
  2111.     this.setVoice = cusr_setvoice;
  2112.     this.setBan = cusr_setban;
  2113.     this.kick = cusr_kick;
  2114.     this.kickBan = cusr_kban;
  2115.     this.say = cusr_say;
  2116.     this.notice = cusr_notice;
  2117.     this.act = cusr_act;
  2118.     this.whois = cusr_whois;
  2119.     this.parent = parent;
  2120.     this.isOp = (typeof isOp != "undefined") ? isOp : false;
  2121.     this.isVoice = (typeof isVoice != "undefined") ? isVoice : false;
  2122.     this.TYPE = "IRCChanUser";
  2123.     
  2124.     parent.users[nick] = this;
  2125.  
  2126.     return this;
  2127. }
  2128.  
  2129. function cusr_geturl ()
  2130. {
  2131.     return this.parent.parent.getURL() + escape(this.nick) + ",isnick";
  2132. }
  2133.  
  2134. function cusr_setop (f)
  2135. {
  2136.     var server = this.parent.parent;
  2137.     var me = server.me;
  2138.  
  2139.     if (!this.parent.users[me.nick].isOp)
  2140.         return false;
  2141.  
  2142.     var modifier = (f) ? " +o " : " -o ";
  2143.     server.sendData("MODE " + this.parent.name + modifier + this.nick + "\n");
  2144.  
  2145.     return true;
  2146.     
  2147. }
  2148.  
  2149. function cusr_setvoice (f)
  2150. {
  2151.     var server = this.parent.parent;
  2152.     var me = server.me;
  2153.  
  2154.     if (!this.parent.users[me.nick].isOp)
  2155.         return false;
  2156.     
  2157.     var modifier = (f) ? " +v " : " -v ";
  2158.     server.sendData("MODE " + this.parent.name + modifier + this.nick + "\n");
  2159.  
  2160.     return true;
  2161.     
  2162. }
  2163.  
  2164. function cusr_kick (reason)
  2165. {
  2166.     var server = this.parent.parent;
  2167.     var me = server.me;
  2168.  
  2169.     reason = (typeof reason != "undefined") ? reason : this.nick;
  2170.     if (!this.parent.users[me.nick].isOp)
  2171.         return false;
  2172.     
  2173.     server.sendData("KICK " + this.parent.name + " " + this.nick + " :" +
  2174.                     reason + "\n");
  2175.  
  2176.     return true;
  2177.     
  2178. }
  2179.  
  2180. function cusr_setban (f)
  2181. {
  2182.     var server = this.parent.parent;
  2183.     var me = server.me;
  2184.  
  2185.     if (!this.parent.users[me.nick].isOp)
  2186.         return false;
  2187.  
  2188.     if (!this.host)
  2189.         return false;
  2190.  
  2191.     var modifier = (f) ? " +b " : " -b ";
  2192.     modifier += this.getHostMask() + " ";
  2193.     
  2194.     server.sendData("MODE " + this.parent.name + modifier + "\n");
  2195.  
  2196.     return true;
  2197.     
  2198. }
  2199.  
  2200. function cusr_kban (reason)
  2201. {
  2202.     var server = this.parent.parent;
  2203.     var me = server.me;
  2204.  
  2205.     if (!this.parent.users[me.nick].isOp)
  2206.         return false;
  2207.  
  2208.     if (!this.host)
  2209.         return false;
  2210.  
  2211.     reason = (typeof reason != "undefined") ? reason : this.nick;
  2212.     var modifier = " -o+b " + this.nick + " " + this.getHostMask() + " ";
  2213.     
  2214.     server.sendData("MODE " + this.parent.name + modifier + "\n" +
  2215.                     "KICK " + this.parent.name + " " + this.nick + " :" +
  2216.                     reason + "\n");
  2217.  
  2218.     return true;
  2219.     
  2220. }
  2221.  
  2222. function cusr_say (msg)
  2223. {
  2224.  
  2225.     this.__proto__.say (msg);
  2226.  
  2227. }
  2228.  
  2229. function cusr_notice (msg)
  2230. {
  2231.  
  2232.     this.__proto__.notice (msg);
  2233.     
  2234. }
  2235.  
  2236. function cusr_act (msg)
  2237. {
  2238.  
  2239.     this.__proto__.act (msg);
  2240.     
  2241. }
  2242.  
  2243. function cusr_whois ()
  2244. {
  2245.  
  2246.     this.__proto__.whois ();
  2247.  
  2248. }
  2249.